<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
  <div :class="[prefixCls]">
    <div
      :class="classes"
      @click="handleClick"
      @drop.prevent="onDrop"
      @paste="handlePaste"
      @dragover.prevent="dragOver = true"
      @dragleave.prevent="dragOver = false">
      <input
        ref="input"
        type="file"
        :class="[prefixCls + '-input']"
        @change="handleChange"
        :multiple="multiple"
        :accept="accept">
      <slot>
        <div
          v-if="type === 'drag' || type === 'drag-1'"
          :class="dragContentClasses">
          <div>
            <Icon type="upload-to-cloud"></Icon>
            <span :class="[prefixCls + '-content-drag-default-text-details']">拖拽文件到此处 或者</span>
            <span :class="[prefixCls + '-content-drag-default-text-strong']">点击上传</span>
          </div>
        </div>
      </slot>
    </div>
    <slot name="tip"></slot>
    <upload-list
      v-if="showUploadList"
      :files="fileList"
      @on-file-remove="handleRemove"
      @on-file-preview="handlePreview">
      <template v-slot:details="{file}">
        <slot
          name="fileDetails"
          :file="file"></slot>
      </template>
    </upload-list>
  </div>
</template>
<script>
import UploadList from './upload-list.vue';
import ajax from './ajax';
import { oneOf } from '../../utils/assist';
import mixinsForm from '../../mixins/form';
import Icon from '../icon/icon.vue';

const prefixCls = 'haloe-upload';

export default {
  name: 'Upload',
  mixins: [ mixinsForm ],
  components: { UploadList, Icon },
  props: {
    action: {
      type: String,
      required: true
    },
    headers: {
      type: Object,
      default () {
        return {};
      }
    },
    multiple: {
      type: Boolean,
      default: false
    },
    data: {
      type: Object
    },
    name: {
      type: String,
      default: 'file'
    },
    withCredentials: {
      type: Boolean,
      default: false
    },
    showUploadList: {
      type: Boolean,
      default: true
    },
    type: {
      type: String,
      validator (value) {
        return oneOf(value, ['select', 'drag', 'drag-1']);
      },
      default: 'select'
    },
    format: {
      type: Array,
      default () {
        return [];
      }
    },
    accept: {
      type: String,
      default: '',
    },
    maxSize: {
      type: Number
    },
    beforeUpload: Function,
    onProgress: {
      type: Function,
      default () {
        return {};
      }
    },
    onSuccess: {
      type: Function,
      default () {
        return {};
      }
    },
    onError: {
      type: Function,
      default () {
        return {};
      }
    },
    onRemove: {
      type: Function,
      default () {
        return {};
      }
    },
    onPreview: {
      type: Function,
      default () {
        return {};
      }
    },
    onExceededSize: {
      type: Function,
      default () {
        return {};
      }
    },
    onFormatError: {
      type: Function,
      default () {
        return {};
      }
    },
    defaultFileList: {
      type: Array,
      default() {
        return [];
      }
    },
    paste: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    webkitdirectory: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      prefixCls: prefixCls,
      dragOver: false,
      fileList: [],
      tempIndex: 1
    };
  },
  computed: {
    mainClasses () {
      return [`${prefixCls}`, {
        [`${prefixCls}-drag-main`]: this.type === 'drag',
        [`${prefixCls}-drag-1-main`]: this.type === 'drag-1'
      }]
    },
    dragContentClasses () {
      return {
        [`${prefixCls}-content-drag-default`]: this.type === 'drag',
        [`${prefixCls}-content-drag-1-default`]: this.type === 'drag-1'
      }
    },
    classes () {
      return [
        `${prefixCls}`,
        {
          [`${prefixCls}-select`]: this.type === 'select',
          [`${prefixCls}-drag`]: this.type === 'drag' || this.type === 'drag-1',
          [`${prefixCls}-dragOver`]: (this.type === 'drag' || this.type === 'drag-1') && this.dragOver
        }
      ]
    }
  },
  methods: {
    handleClick () {
      if (this.itemDisabled) return;
      this.$refs.input.click();
    },
    handleChange (e) {
      const files = e.target.files;
      if (!files) {
        return;
      }
      this.uploadFiles(files);
      this.$refs.input.value = null;
    },
    onDrop (e) {
      this.dragOver = false;
      if (this.itemDisabled) return;
      this.uploadFiles(e.dataTransfer.files);
    },
    handlePaste (e) {
      if (this.itemDisabled) return;
      if (this.paste) {
        this.uploadFiles(e.clipboardData.files);
      }
    },
    uploadFiles (files) {
      let postFiles = Array.prototype.slice.call(files);
      if (!this.multiple) postFiles = postFiles.slice(0, 1);
      if (postFiles.length === 0) return;
      postFiles.forEach(file => {
        this.upload(file);
      });
    },
    upload (file) {
      if (!this.beforeUpload) {
        return this.post(file);
      }
      const before = this.beforeUpload(file);
      if (before && before.then) {
        before.then(processedFile => {
          if (Object.prototype.toString.call(processedFile) === '[object File]') {
            this.post(processedFile);
          } else {
            this.post(file);
          }
        }, () => {});
      } else if (before !== false) {
        this.post(file);
      }
    },
    post (file) {
      if (this.format.length) {
        const _file_format = file.name.split('.').pop().toLocaleLowerCase();
        const checked = this.format.some(item => item.toLocaleLowerCase() === _file_format);
        if (!checked) {
          this.onFormatError(file, this.fileList);
          return false;
        }
      }
      // check maxSize
      if (this.maxSize) {
        if (file.size > this.maxSize * 1024) {
          this.onExceededSize(file, this.fileList);
          return false;
        }
      }

      this.handleStart(file);
      let formData = new FormData();
      formData.append(this.name, file);

      ajax({
        headers: this.headers,
        withCredentials: this.withCredentials,
        file: file,
        data: this.data,
        filename: this.name,
        action: this.action,
        onProgress: e => {
          this.handleProgress(e, file);
        },
        onSuccess: res => {
          this.handleSuccess(res, file);
        },
        onError: (err, response) => {
          this.handleError(err, response, file);
        }
      });
    },
    handleStart (file) {
      file.uid = Date.now() + this.tempIndex++;
      const _file = {
        status: 'uploading',
        name: file.name,
        size: file.size,
        percentage: 0,
        uid: file.uid,
        showProgress: true
      };
      this.fileList.push(_file);
      return _file;
    },
    getFile (file) {
      const fileList = this.fileList;
      let target;
      fileList.every(item => {
        target = file.uid === item.uid ? item : null;
        return !target;
      });
      return target;
    },
    handleProgress (e, file) {
      const _file = this.getFile(file);
      this.onProgress(e, _file, this.fileList);
      _file.percentage = e.percent || 0;
    },
    handleSuccess (res, file) {
      const _file = this.getFile(file);
      if (_file) {
        _file.status = 'finished';
        _file.response = res;

        this.onSuccess(res, _file, this.fileList);
        this.handleFormItemChange('change', _file);
        setTimeout(() => {
          _file.showProgress = false;
        }, 1000);
      }
    },
    handleError (err, response, file) {
      const _file = this.getFile(file);
      _file.status = 'fail';
    },
    handleRemove(file) {
      if (file.status === 'uploading') {
        file.xhr.abort()
      }
      const fileList = this.fileList;
      fileList.splice(fileList.indexOf(file), 1);
      this.onRemove(file, fileList);
    },
    handlePreview(file) {
      if (file.status === 'finished') {
        this.onPreview(file);
      }
    },
  },
  watch: {
    defaultFileList: {
      immediate: true,
      handler(fileList) {
        this.fileList = fileList.map(item => {
          item.status = 'finished';
          item.percentage = 100;
          item.uid = Date.now() + this.tempIndex++;
          return item;
        });
      }
    }
  },
};
</script>
